package com.mooral.bean;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Locale;
import java.util.regex.Pattern;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.mooral.context.Context;
import com.mooral.dataAccess.DataAccessConnection;
import com.mooral.web.action.MooralAction;

public class Bean extends Hashtable implements Serializable{

	
	protected static Log log = LogFactory.getLog(Bean.class);
	
 /**
  * 
  */
 private static final long serialVersionUID = -8511667161316279967L;

 	protected String objectType = "Bean";
 	protected boolean populated = false;
 
	public boolean isPopulated() {
		return populated;
	}


	public void setPopulated(boolean populated) {
		this.populated = populated;
	}


	/**
	 * Dado un string, retorna la version slug del mismo.  
	 * @param String input
	 * @return String
	 */
	public String toSlug(String input) {

		Pattern NONLATIN = Pattern.compile("[^\\w-]");
		Pattern WHITESPACE = Pattern.compile("[\\s]");

		String nowhitespace = WHITESPACE.matcher(input).replaceAll("-");
		String normalized = Normalizer.normalize(nowhitespace, Form.NFD);
		String slug = NONLATIN.matcher(normalized).replaceAll("");
		return slug.toLowerCase(Locale.ENGLISH);
	}


	/**
	 * Genera dinamicamente las llamadas a los Setters
	 */
	public Object put(Object key, Object value){
		 Method m[] = this.getClass().getDeclaredMethods();
         for (int i = 0; i < m.length; i++){
        	 if (m[i].getName().equals(getMethodName(key.toString(), "set") )){
        		 try { // TODO hacer el cast para los tipos de datos
					return m[i].invoke(this, new Object[]{value});
				} catch (Exception e) {
					return null;
				}
        	 }
         }
         return super.put(key, value);
	}
 	
 
	/**
	 * Genera dinamicamente las llamadas a los Getters
	 */
 	public Object get(Object key){
 		 Method m[] = this.getClass().getDeclaredMethods();
         for (int i = 0; i < m.length; i++){
        	 if (m[i].getName().equals(getMethodName(key.toString(), "get") )){
        		 try {
					return m[i].invoke(this, new Object[]{});
				} catch (Exception e) {
					return null;
				}
        	 }
         }
         return super.get(key);
 	}
 	

 /**
  *    
  */
 	private String getMethodName(String attributeName, String methodType){
 		return methodType + attributeName.substring(0, 1).toUpperCase() + attributeName.substring(1); 
 	}
 	
 	
 /**
  * Dado un resulset, lo carga en el bean.  
  */
	 public void populate(ResultSet resultSet) throws SQLException {
		 
		 
	    // Build a list of relevant column properties from this resultSet
	    HashMap properties = new HashMap();
	    // Acquire resultSet MetaData
	    ResultSetMetaData metaData = resultSet.getMetaData();
	    int cols = metaData.getColumnCount();
	    
	    // Scroll to next record and pump into hashmap
	    /** 
	     * mradosta 2010-03-19
	     * TODO: 
	     * Don't know why, but if recorset returns just one record, error is thrown.
	     * Must move the recorset, but if more than one record is returned, there's no error.
	     * Why ???   
	     * 
	     */
    	for (int i=1; i<=cols ; i++) {
    		/*try {
    			log.debug(metaData.getColumnName(i));
    			log.debug(resultSet.getObject(metaData.getColumnName(i)));
    		} catch (Exception e) {
    			resultSet.next();
    		}*/
    		Object value = resultSet.getObject(metaData.getColumnLabel(i));
    		if(value != null)properties.put(metaData.getColumnLabel(i), value);
    	}
	    
	    // Set the corresponding properties of our bean
	    try {
	    	BeanUtils.populate(this, properties);
	    	this.setPopulated(true);
	    } catch (Exception e) {
	    	throw new SQLException("BeanUtils.populate threw " + e.toString());
	    }
 	}
	 
	 
	 public void populate(String query) throws Exception {
		 
			Context context = new Context();
			 
			DataAccessConnection dataAccessConnection = context.getDataAccessConnection();
			ResultSet resultSet = dataAccessConnection.getResultSetQuery(query);
			 
			if(resultSet.next())
				this.populate(resultSet);
			
			dataAccessConnection.disposeConnection();
	 }
	 
	 
	 public Object clone(){
		 try {
			 return BeanUtils.cloneBean(this);		 
		 } catch (Exception e) {
		 }
		 return null;
	 }


}